#include "74hc595.h"
#include <math.h>

unsigned char digit_tab[] =
{
0x3f,//0
0x06,//1
0x5b,//2
0x4f,//3
0x66,//4
0x6d,//5
0x7d,//6
0x07,//7
0x7f,//8
0x6f,//9
0x77,//a
0x7c,//b
0x39,//c
0x5e,//d
0x79,//e
0x71,//f
0x00,//全关
};

unsigned char digit_choose_bit[] = {
0xfe,0xfd,0xfb,0xf7,
0xef,0xdf,0xbf,0x7f,
};

#define PIN_RESET(dev)                          \
    do{                                         \
        dev->set_dev_data(dev->pin_clk,0);      \
        dev->set_dev_data(dev->pin_st,0);       \
    }while(0)

#define ST_REFRESH(dev)                         \
    do{                                         \
        dev->set_dev_data(dev->pin_st,0);       \
        dev->set_dev_data(dev->pin_st,1);       \
    }while(0)

void hc595_init(digit_dev_t* dev)
{
    dev->gpio_init(dev->pin_clk, GPIO_OUTPUT | GPIO_PULLUP | GPIO_SMT_EN | GPIO_DRV_0);
    dev->gpio_init(dev->pin_ser, GPIO_OUTPUT | GPIO_PULLUP | GPIO_SMT_EN | GPIO_DRV_0);
    dev->gpio_init(dev->pin_st, GPIO_OUTPUT | GPIO_PULLUP | GPIO_SMT_EN | GPIO_DRV_0);

    PIN_RESET(dev);

}

/**
 * @brief 发送 一字节数据到595串口引脚
 * @param  dev              
 * @param  data             
 */
static void hc595_send_char_impl(digit_dev_t* dev, unsigned char data)
{
    for (int i = 0; i < 8;i++) {
        //从高位写入
        if (data >= 0x80) {
            dev->set_dev_data(dev->pin_ser, 1);
        }
        else {
            dev->set_dev_data(dev->pin_ser, 0);
        }
        //上升沿触发写入
        dev->set_dev_data(dev->pin_clk, 0);
        dev->set_dev_data(dev->pin_clk, 1);
        data <<= 1;
    }
}

/**
 * @brief 计算小数点位数
 * @param  number           
 * @return size_t 
 */
static size_t deimal_size(double number)
{
    size_t size = 0;
    while ((number - (int)number) != 0) {
        number *= 10;
        size++;
    }
    return size;
}
void hc595_send_byte2(digit_dev_t* dev, unsigned char dataH, unsigned char dataL)
{
    PIN_RESET(dev);
    hc595_send_char_impl(dev, dataH);
    hc595_send_char_impl(dev, dataL);
    ST_REFRESH(dev);
    PIN_RESET(dev);
}

/**
 * @brief 更新buf到锁存器中
 * @param  dev              
 * @param  buf              
 */
static void hc595_refresh_buf(digit_dev_t* dev, unsigned char* buf)
{
    static int index = 0;
    for (;index < 8;index++) {
        //消隐
        hc595_send_byte2(dev, 0xff, 0x00);
        hc595_send_byte2(dev, digit_choose_bit[index], buf[index]);
        dev->delay_fun(2000);
    }
    index = 0;
}

void hc595_desplay(digit_dev_t* dev, double number)
{
    PIN_RESET(dev);
    //默认0x00不显示
    unsigned char buf[8] = { 0x00 };

    if (number > 99999999 || number < -9999999) {
        printf("show range: -9999999 ~ 99999999");
        for (int i = 0; i < 8;i++) {
            buf[i] = MINUS;
        }
        //显示--------
        hc595_refresh_buf(dev, buf);
        return;
    }

    size_t size = deimal_size(number);

    if (size != 0) {
        //把number处理成整数，小数点写入buf中再处理
        number = number * pow(10, size);
    }

    long integer = number;

    uint8_t is_negative = integer < 0;
    //负数要去相反数，避免后面计算每一位的余数为负数
    if (is_negative) {
        integer = -integer;
    }
    int i = 0;
    for (; integer != 0;i++) {
        buf[7 - i] = digit_tab[integer % 10];
        //处理小数点
        if (size != 0 && i == size) {
            buf[7 - i] = DP_WRAP(buf[7 - i]);
        }
        integer /= 10;
    }
    //如果是负数在buf最前面加上负号
    if (is_negative) {
        buf[7 - i] = MINUS;
    }

    hc595_refresh_buf(dev, buf);
}
